package pers.vic.sso.server.controller;

import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import pers.vic.boot.base.model.BaseResponse;
import pers.vic.boot.base.vo.BooleanWithMsg;
import pers.vic.sso.common.model.AccessTokenContent;
import pers.vic.sso.common.model.RefreshTokenContent;
import pers.vic.sso.common.model.RpcAccessToken;
import pers.vic.sso.common.model.SsoUser;
import pers.vic.sso.common.constant.Oauth2Constant;
import pers.vic.sso.server.service.AppService;
import pers.vic.sso.server.service.Oauth2Service;
import pers.vic.sso.server.session.RefreshTokenManager;
import pers.vic.sso.server.session.TicketGrantingTicketManager;

import javax.annotation.Resource;

/**
 * 描述:
 * Oauth2 controller
 *
 * @author Vic.xu
 * @date 2021-11-02 10:29
 */
@RestController
public class Oauth2Controller extends BaseSsoController {

    @Resource
    private AppService appService;

    @Resource
    private Oauth2Service oauth2Service;

    @Resource
    private RefreshTokenManager refreshTokenManager;

    @Resource
    private TicketGrantingTicketManager ticketGrantingTicketManager;


    /**
     * 通过授权码(ticket/code)获取accessToken以及用户信息
     */
    @RequestMapping(value = Oauth2Constant.ACCESS_TOKEN_URL)
    public BaseResponse<RpcAccessToken> accessToken(@RequestParam(value = Oauth2Constant.GRANT_TYPE, required = true) String grantType,
                                    @RequestParam(value = Oauth2Constant.APP_ID, required = true) String appId,
                                    @RequestParam(value = Oauth2Constant.APP_SECRET, required = true) String appSecret,
                                    @RequestParam(value = Oauth2Constant.AUTH_CODE, required = false) String code,
                                    @RequestParam(value = Oauth2Constant.USERNAME, required = false) String username,
                                    @RequestParam(value = Oauth2Constant.PASSWORD, required = false) String password) {
        //参数检验
        BooleanWithMsg msg = oauth2Service.validateParam(grantType, code, username, password);
        if (!msg.isSuccess()) {
            return BaseResponse.error(msg.getMessage());
        }

        //应用校验
        BooleanWithMsg appIdMsg = appService.validate(appId, appSecret);
        if (!appIdMsg.isSuccess()) {
            return BaseResponse.error(appIdMsg.getMessage());
        }

        // 校验授权码 并生成token
        BaseResponse<RpcAccessToken> data = oauth2Service.validateAndGenerateToken(grantType, code, username, password, appId);

        return data;
    }

    /**
     * 利用refreshToken刷新accessToken，并延长TGT超时时间  <br />
     * <p>
     * accessToken刷新结果有两种：
     * 1. 若accessToken已超时，那么进行refreshToken会生成一个新的accessToken，新的超时时间；
     * 2. 若accessToken未超时，那么进行refreshToken不会改变accessToken，但超时时间会刷新，相当于续期accessToken。
     * </p>
     *
     * @param appId
     * @param refreshToken
     * @return
     */
    @RequestMapping(value = Oauth2Constant.REFRESH_TOKEN_URL)
    public BaseResponse<RpcAccessToken> refreshToken(
            @RequestParam(value = Oauth2Constant.APP_ID, required = true) String appId,
            @RequestParam(value = Oauth2Constant.REFRESH_TOKEN, required = true) String refreshToken) {

        if (!appService.checkAppId(appId)) {
            return BaseResponse.error("非法应用");
        }
        RefreshTokenContent refreshTokenContent = refreshTokenManager.validate(refreshToken);
        if (refreshTokenContent == null) {
            return BaseResponse.error("refreshToken错误或已过期");
        }
        AccessTokenContent accessTokenContent = refreshTokenContent.getAccessTokenContent();
        if (!StringUtils.equals(appId, accessTokenContent.getAppId())) {
            return BaseResponse.error("非法应用");
        }


        SsoUser user = ticketGrantingTicketManager.getAndRefresh(accessTokenContent.getAuthorizationCode().getTgt());
        if (user == null) {
            return BaseResponse.error("服务端session已过期");
        }

        RpcAccessToken rpcAccessToken = oauth2Service.generateRpcAccessToken(accessTokenContent, refreshTokenContent.getAccessToken());
        BaseResponse<RpcAccessToken> data = BaseResponse.success(rpcAccessToken);
        return data;

    }
}